﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Collections;
using Collections.TreeCollections;
using System.Xml.Linq;
using FeatureModelCore.FMTypes;
using System.IO;
using FeatureModelCore.FMInterface;
using FeatureModelCore.FMCrossTreeConstraint;

namespace FeatureModelCore.Configuration
{
    public class ConfigFeatureModel : Tree
    {
        #region Feilds
        private FMSet<ConfigConstraint> _constraintsList;
        #endregion

        #region Constructor
        static ConfigFeatureModel()
        {
            // register tree
            TreeRegisterClass treeClass = new TreeRegisterClass(typeof(ConfigFeatureModel)
    , ConfigFeatureModel.ConstructConfigure
    , ConfigFeatureModel.ConstructConfigureWithRoot
    , ConfigFeatureModel.XmlParser);
            // Configure Node
            TreeObjectRegisterClass configureNode = new TreeObjectRegisterClass(
                typeof(ConfigNode), ConfigNode.ConstructNode, ConfigNode.xmlParser);
            treeClass.RegisterTreeObjectType(configureNode);

            // Configure Root
            TreeObjectRegisterClass configureRoot = new TreeObjectRegisterClass(
                typeof(ConfigRoot), ConfigRoot.ConstructRoot
                , ConfigRoot.xmlParser);
            treeClass.RegisterTreeObjectType(configureRoot);

            // Relationship Configure
            TreeObjectRegisterClass configureRelationship = new TreeObjectRegisterClass(
                typeof(ConfigRelationship), ConfigRelationship.ConstructGroupRelationship
                , ConfigRelationship.xmlParser);
            treeClass.RegisterTreeObjectType(configureRelationship);

            TreeRegisterTable.TreeClassRegisterTable.RegisterTreeClass(treeClass);
        }
        #endregion

        #region Static Constructor
        /// <summary>
        /// Construct Configure
        /// </summary>
        /// <param name="args"></param>
        /// <returns></returns>
        public static ConfigFeatureModel ConstructConfigure(object[] args)
        {
            return new ConfigFeatureModel();
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="args"></param>
        /// <returns></returns>
        public static ConfigFeatureModel ConstructConfigureWithRoot(TreeRoot root, object[] args)
        {
            ConfigRoot cRoot = root as ConfigRoot;
            return new ConfigFeatureModel(cRoot);
        }
        #endregion

        #region Constructor
        /// <summary>
        /// 
        /// </summary>
        public ConfigFeatureModel() : base()
        {
            _constraintsList = new FMSet<ConfigConstraint>();
        }
        public ConfigFeatureModel(ConfigRoot root)
            : base(root)
        {
            _constraintsList = new FMSet<ConfigConstraint>();
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="fmTree"></param>
        public ConfigFeatureModel(FeatureModelTree fmTree) : base()
        {
            // construct configuration from feature model
            _constraintsList = new FMSet<ConfigConstraint>();
            loadFromFeatureModel(fmTree);
        }
        #endregion

        #region XML Parser
        /// <summary>
        /// To XDocument
        /// </summary>
        /// <returns></returns>
        public override XDocument ToXDoc()
        {
            return base.ToXDoc();
        }
        /// <summary>
        /// Parse XDocument to FeatureModel
        /// </summary>
        /// <param name="xdoc"></param>
        /// <returns></returns>
        public static FeatureModelTree XDocParser(XDocument xdoc)
        {
            FeatureModelTree fmTree = (FeatureModelTree)Collections.Tree.XDocParser(
                typeof(FeatureModelTree), xdoc);
            return fmTree;
        }
        /// <summary>
        /// To Xelem
        /// </summary>
        /// <returns></returns>
        public override XElement ToXElem()
        {
            XElement xelem = base.ToXElem();
            foreach (ConfigConstraint cons in _constraintsList)
            {
                xelem.Add(cons.ToXElem());
            }
            return xelem;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="xelem"></param>
        /// <param name="register"></param>
        /// <returns></returns>
        public static void XmlParser(XElement xelem, Tree tree)
        {
            ConfigFeatureModel fmTree = tree as ConfigFeatureModel;
            if (fmTree == null)
            {
                throw new Exception();
            }

            foreach (XElement xcons in xelem.Elements(typeof(CrossTreeConstraint).Name))
            {
                ConfigConstraint cons = ConfigConstraint.XmlParser(xcons);
                fmTree._constraintsList.Add(cons);
                cons.Configuration = fmTree;
            }
        }
        #endregion

        private void loadFromFeatureModel(FeatureModelTree fmTree)
        {
            ConfigRoot root = (ConfigRoot)this._root;
            FeatureTreeRoot fmRoot = (FeatureTreeRoot)fmTree.Root;
            root.NodeName = fmRoot.FeatureName;
            subLoadFromFeatureModel(root, fmRoot);
            
            // create constraint
            foreach (CrossTreeConstraint cons in fmTree.AllCrossTreeConstraints)
            {
                ConfigNode nodeA = GetConfigNode(cons.NodeA.FeatureName);
                ConfigNode nodeB = GetConfigNode(cons.NodeB.FeatureName);

                ConstraintType consType = ConstraintType.Exclude;
                if (consType == ConstraintType.Require)
                {
                    consType = ConstraintType.Require;
                }
                ConfigConstraint configCons = new ConfigConstraint(nodeA.ID
                    , nodeB.ID, consType);
                this._constraintsList.Add(configCons);
            }
        }
        private void subLoadFromFeatureModel(TreeObject configObj, TreeObject fmObj)
        {
            if (fmObj is IFeature)
            {
                IFeature ifeatureNode = (IFeature)fmObj;
                IRelation[] grList = ifeatureNode.ChildrenGroupRelationshipes;

                foreach (FeatureTreeGroupRelationship gr in grList)
                {
                    RelationshipConfigureType type;
                    switch (gr.Type)
                    {
                        case FMTreeGroupRelationshipType.GroupMandatory:
                            {
                                type = RelationshipConfigureType.Manadory;
                                break;
                            }
                        case FMTreeGroupRelationshipType.GroupOptional:
                            {
                                type = RelationshipConfigureType.Optional;
                                break;
                            }
                        case FMTreeGroupRelationshipType.Alternative:
                            {
                                type = RelationshipConfigureType.Alternative;
                                break;
                            }
                        case FMTreeGroupRelationshipType.Or:
                            {
                                type = RelationshipConfigureType.Or;
                                break;
                            }
                        default:
                            {
                                throw new Exception();
                            }
                    }

                    TreeObject newConfigObj = this.CreateNewInstance(typeof(ConfigRelationship)
                                , (INonLeaf)configObj, type);
                    subLoadFromFeatureModel(newConfigObj, gr);
                }
                return;
            }
            else if (fmObj is FeatureTreeGroupRelationship)
            {
                FeatureTreeGroupRelationship gr = (FeatureTreeGroupRelationship)fmObj;
                ConfigRelationship configR = (ConfigRelationship)configObj;
                foreach (FeatureTreeNode node in gr.ChildrenNodes)
                {
                    TreeObject newConfigObj = this.CreateNewInstance(typeof(ConfigNode), configR
                        , node.FeatureName);
                    subLoadFromFeatureModel(newConfigObj, node);
                }
            }
        }

        public override object Clone()
        {
            throw new NotImplementedException();
        }


        /// <summary>
        /// Get Config Node Base on Node Name
        /// </summary>
        /// <param name="nodeName"></param>
        /// <returns></returns>
        public ConfigNode GetConfigNode(string nodeName)
        {
            foreach (TreeObject obj in this.AllTreeObjects)
            {
                if (obj is ConfigNode)
                {
                    if (((ConfigNode)obj).Name == nodeName)
                    {
                        return (ConfigNode)obj;
                    }
                }
            }
            return null;
        }

        public static ConfigFeatureModel Load(string fullFilePath)
        {
            string file = string.Empty;
            using (StreamReader reader = File.OpenText(fullFilePath))
            {
                file = reader.ReadToEnd();
            }
            XDocument xdoc = XDocument.Parse(file);
            return (ConfigFeatureModel)Tree.XDocParser(typeof(ConfigFeatureModel), xdoc);
        }
    }
}
